home *** CD-ROM | disk | FTP | other *** search
- /*
- ***********************************************************************
- *
- * Simple Window Class
- * A generic simple window that can be dragged around the screen
- * and closed by clicking a "go-away" button
- * No other events are handled, redefine the event handler if necessary.
- *
- *
- ***********************************************************************
- */
-
- #include "image.h"
- #include "window.h"
- #include <Palettes.h>
- #include "EventHandlers.h"
-
- /*
- *----------------------------------------------------------------------
- * Service functions
- */
-
- // Getting a bounding rectangle of an image
- ScreenRect::ScreenRect(const IMAGE& image)
- {
- left = top = 0;
- right = image.q_ncols();
- bottom = image.q_nrows();
- }
-
- #if 0
-
- // Create a rectangle of given height/width
- // positioned at the origin
- ScreenRect::ScreenRect(const rowcol& heightwidth)
- {
- left = top = 0;
- bottom = heightwidth.row();
- right = heightwidth.col();
- }
-
- // Create a rectangle of given height/width
- // positioned at a given point
- ScreenRect::ScreenRect(const rowcol& origin, const rowcol& heightwidth)
- {
- top = origin.row();
- left = origin.col();
- bottom = top+heightwidth.row();
- right = left+heightwidth.col();
- }
- #endif
-
-
- // Shifting a rectangle by the same amount
- // in X and Y
- ScreenRect& ScreenRect::operator += (const int offset)
- {
- top += offset; bottom += offset; left += offset; right += offset;
- return *this;
- }
-
-
- // Print what rectangle is this
- void ScreenRect::print(const char * title) const
- {
- alert("Rectangle %s (%d,%d) - (%d,%d)",title,top,left,bottom,right);
- }
-
-
- #if 0
- /*
- *----------------------------------------------------------------------
- * Primitive Window creators/destructors
- */
-
- // This is a protected constructor. Normally
- // objects XVTBasicWindow are created as a part
- // of a derived class
- XVTBasicWindow::XVTBasicWindow(void)
- {
- Not_canceled = FALSE;
- this_window = NULL_WIN;
- }
-
- // Destroy the window if it hasn't been destroyed
- // already
- XVTBasicWindow::~XVTBasicWindow(void)
- {
- if( this_window != 0 )
- close_window(this_window);
- }
-
- // Close the window
- void XVTBasicWindow::cancel(void)
- {
- Not_canceled = FALSE;
- if( this_window != 0 )
- close_window(this_window);
- }
-
- /*
- *----------------------------------------------------------------------
- * Basic event dispatching/handling
- */
-
- // A universal event handler: does almost nothing
- // but watches creation and destruction of windows
- // (to tell when there are no windows to watch)
- // For any other tasks, it calls the handler of
- // a particular window
- int XVTBasicWindow::no_opened_windows = 0;
- long XVTBasicWindow::universal_handler(WINDOW win, EVENT *ep)
-
- {
- XVTBasicWindow& the_window = *(XVTBasicWindow *)get_app_data(win);
-
- if( ep->type == E_CREATE )
- no_opened_windows++, the_window.this_window = win;
-
- the_window.handler(*ep);
-
- // XVT would destroy the window after this
- // event
- if( ep->type == E_DESTROY )
- no_opened_windows--, the_window.this_window = 0;
-
- return 0;
- }
-
- // Serve the windows until all get closed
- void XVTBasicWindow::serve_to_death(void)
- {
- while( no_opened_windows>0 )
- process_events();
- }
-
-
- // Event handler for a particular window
- // Finally, a handler which really does smth
- // specific
- void XVTBasicWindow::handler(EVENT event)
- {
- switch(event.type)
- {
- case E_CREATE:
- if( !initialize() )
- cancel();
- break;
- }
- }
-
- /*
- *----------------------------------------------------------------------
- * Window creators
- *
- * Note, event processing may start *before* the create_* functions return!
- * So, if initialize() or event handlers, etc rely on some data that are
- * going to be initialized *after* create_* function, you're in trouble!
- * It's better to call create() function the last thing in the constructor,
- * or even not to call at all (call it separetely later) to allow derived
- * classes initialize properly!
- *
- */
- #endif
-
- /*
- *----------------------------------------------------------------------
- * Window creators
- */
-
- // Create a color window of the specified size and title
- // Note, the upper left corner of 'rect' must
- // NOT be (0,0)!
- // The window is created hidden; call init() to finish
- // the processing and show the window
- // Kepp in mind that the window is hidden right after
- // the construction
- // Call init() to fincish the construction and reveal it
- ScreenWindow::ScreenWindow(ScreenRect rect, const char * title)
- : private_palette(nil)
- {
- const int window_offset = 100; // Cannot be 0 or small!
- this_window = NewCWindow(nil,rect += window_offset,(Pstr)title,
- FALSE,noGrowDocProc,
- (WindowPtr)(-1),TRUE,(long)this);
- assert( this_window != 0 );
- SetPort(this_window);
- SelectWindow(this_window);
- }
-
- // Create a color window from a resource template
- ScreenWindow::ScreenWindow(const short resource_id)
- : private_palette(nil)
- {
- this_window = GetNewCWindow(resource_id,nil,(WindowPtr)(-1));
- SetWRefCon(this_window,(long)this);
- assert( this_window != 0 );
- SetPort(this_window);
- SelectWindow(this_window);
- }
-
-
- // I wouldn't've needed this is destructor had been really virtual. Right now,
- // CW 6.0 accepts attribute virtual for the destructor, but doesn't override
- // destructors as it does for virtual functions. Oh, well, another kludge
-
- // Close the window and clean up the rubble
- void ScreenWindow::destroy_it(void)
- {
- assert( this_window != nil );
- if( private_palette != nil ) // If there was a private palette allocated
- {
- if( GetPalette(this_window) == private_palette )
- NSetPalette(this_window,nil,pmNoUpdates); // if it was bound to a window, break the bond
- DisposePalette(private_palette);
- private_palette = nil;
- }
- DisposeWindow(this_window);
- this_window = nil;
- }
-
- /*
- *----------------------------------------------------------------------
- * Basic Even Handling
- */
-
- // The top-level event dispatcher
- // Returns FALSE when the window is to be destroyed
- // and thus won't need any further events
- Boolean ScreenWindow::handle_event(const EventRecord& the_event)
- {
- switch( the_event.what )
- {
- case mouseDown:
- {
- WindowPtr wp;
- short windowPart = FindWindow(the_event.where, &wp);
- return handle_mouse_down(the_event,wp,windowPart);
- }
-
- case updateEvt:
- if( this_window == (WindowPtr)the_event.message )
- update();
- break;
-
- case keyDown:
- case autoKey:
- return handle_key_down(the_event);
-
- case EventHandler::mykind:
- return handle_my_event(the_event);
-
- case activateEvt:
- if( this_window == (WindowPtr)the_event.message )
- if( the_event.modifiers & 0x01 )
- SetPort(this_window);
- break;
- }
- return TRUE;
- }
-
- // Handle "mouse-down" events
- // Returns FALSE if the user clicked within the
- // go-away region of our window. Otherwise,
- // returns TRUE
- // Handles dragging if necessary and system clicks
- Boolean ScreenWindow::handle_mouse_down
- (const EventRecord& the_event, WindowPtr where_window, short window_part)
- {
- switch( window_part )
- {
- case inSysWindow:
- SystemClick(&the_event, where_window);
- break;
-
- case inMenuBar:
- break; // Don't handle it yet
-
- case inContent:
- break;
-
- case inDrag:
- if( this_window == where_window )
- DragWindow(this_window,the_event.where,&qd.screenBits.bounds);
- break;
-
- case inGoAway:
- return !TrackGoAway(where_window, the_event.where);
- }
- return TRUE;
- }
-
- // Handles key_down & auto_key events. Return FALSE
- // if the window is to be closed down
- Boolean ScreenWindow::handle_key_down(const EventRecord& the_event)
- {
- return FALSE; // Any key kills the application, sorry
- }
-
-
- // Handles null events, when nothing happens for some
- // time. Return FALSE when it's time to die
- Boolean ScreenWindow::handle_null_event(const long event_time)
- {
- return TRUE; // Keep going
- }
-
-
- // Handles my private events... It's up to the user...
- Boolean ScreenWindow::handle_my_event(const EventRecord& the_event)
- {
- EventHandler::put_back(the_event);
- return FALSE;
- }
-
-
-
- // Private window parts
- // Handle an update event - redraw the window
- // A virtual function draw() is called to do
- // the actual redrawing
- void ScreenWindow::update(void)
- {
- SetNewGrafPtr((GrafPtr)this_window);
- BeginUpdate(this_window);
- draw();
- EndUpdate(this_window);
- }
-
- // Make the entire window being redrawn
- void ScreenWindow::refresh(void)
- {
- InvalRect(&this_window->portRect);
- }
-
- // First step of negotiating color environment for the window:
- // Create a palette based on a given colormap, associate it
- // with the window, and have the PaletteManager negotiate
- // with the graphic device(s) and update the hardware tables
- // accordingly
- void ScreenWindow::set_private_palette(const CTabHandle clut)
- {
- assert( clut != nil && *clut != nil );
-
- private_palette = NewPalette((**clut).ctSize,clut,pmTolerant+pmExplicit,0);
- assert( private_palette != nil );
- SetPalette(this_window,private_palette,TRUE);
- do_well( QDError() );
- ActivatePalette(this_window);
- do_well( QDError() );
- }
-
- /*
- *----------------------------------------------------------------------
- * Color Lookup Table
- */
-
- // Load from a 'clut' resource
- CLUTable::CLUTable(const short clut_id)
- : handle(GetCTable(clut_id)), my_own_handle(true)
- {
- assert( handle != 0 );
- (**handle).ctFlags |= 0x4000; // A Flag that tells QD that this CLUT and a palette
- // are to be synchronized. That is,
- // (**handle).ctTable[i].value is understood as index
- // in the current window palette (rather than a pixel
- // value). That means, no time would be wasted
- // for matching of colors when moving offscreen to onscreen
- // See IM, Advanced Color Imaging, Chap 1,
- // Palette Manager, p. 1-36
- }
-
- // Deep copy of the other clut, which may be null
- // (in the latter case, this clut is also emptied)
- // Because it's a deep copy, it copies even in a CTabHandle
- // we don't own
- // Still, we keep the old CLUT's ctFlags
- void CLUTable::deep_copy_from(const CLUTable& other_clut)
- {
- if( &other_clut == this || other_clut.handle == handle )
- return;
-
- if( other_clut.q_null() ) // Assigning a null CLUT empties this one too
- {
- if(my_own_handle)
- DisposeCTable(handle);
- handle = nil;
- my_own_handle = false;
- return;
- }
-
- if( handle == nil )
- {
- my_own_handle = true;
- handle = other_clut.handle;
- do_well( HandToHand(&(Handle)handle) ); // Clone the other_clut's handle
- }
- else
- {
- const short old_ctFlags = (**handle).ctFlags;
- const int other_size = GetHandleSize((Handle)other_clut.handle);
- assert( other_size > 4 );
- if( (**handle).ctSize != (**other_clut.handle).ctSize )
- SetHandleSize((Handle)handle,other_size);
- HLock((Handle)handle); HLock((Handle)other_clut.handle);
- memcpy(*handle, *other_clut.handle,other_size);
- HUnlock((Handle)handle); HUnlock((Handle)other_clut.handle);
- (**handle).ctFlags = old_ctFlags;
- }
- CTabChanged(handle);
- }
-
- // Dump the whole contents of the CLUT
- void CLUTable::dump(const char title []) const
- {
- if( handle == nil )
- {
- message("\nColor Lookup Table '%s' does not actually exist\n",title);
- return;
- }
-
- message("\nColor Lookup Table '%s' with %s handle",title,
- my_own_handle ? "own" : "borrowed");
-
- message("\nseed %d, flags 0x%0x",(**handle).ctSeed,(**handle).ctFlags);
- message("\nThe table has %d entries, They are:",(**handle).ctSize);
- for(register int i=0; i<(**handle).ctSize; i++)
- {
- const ColorSpec& color_spec = (**handle).ctTable[i];
- message("\n\t%d -> %d:%d:%d",color_spec.value,
- color_spec.rgb.red,color_spec.rgb.green,color_spec.rgb.blue);
- }
- message("\n\n");
- }
-
- // Tell briefly about this CLUT
- void CLUTable::info(const char title []) const
- {
- if( handle == nil )
- {
- message("\nColor Lookup Table '%s' does not actually exist\n",title);
- return;
- }
- message("\nColor Lookup Table '%s' with %s handle",title,
- my_own_handle ? "own" : "borrowed");
-
- message("\nseed %d, flags 0x%0x, %d entries\n",
- (**handle).ctSeed,(**handle).ctFlags,(**handle).ctSize);
- }
-
- /*
- *----------------------------------------------------------------------
- * An off-screen pixel buffer for faster drawing
- */
-
- // Allocate the buffer for offscreen
- // drawing and load a CLUT (if clut_id != 0)
- OffScreenBuffer::OffScreenBuffer(ScreenRect rect, const CLUTable& clut)
- : graf_world(nil)
- {
- do_well( NewGWorld(&graf_world,8,rect, clut.q_null() ? nil : (CTabHandle)clut,nil,0) );
- assert( graf_world != nil );
- // BTW, CLUT has been copied, so 'clut' can be disposed of
-
- // Get hold of the offscreen pixmap
- pixmap = GetGWorldPixMap(graf_world); // and make sure it looks like
- assert( pixmap != nil && *pixmap != nil ); // we can use it
-
- assert( (**pixmap).cmpCount == 1 ); // We have a color table index
- assert( !PixMap32Bit(pixmap) );
-
- _height = abs((**pixmap).bounds.top - (**pixmap).bounds.bottom);
- _width = abs((**pixmap).bounds.right - (**pixmap).bounds.left);
- _bytes_per_row = (**pixmap).rowBytes & 0x7fff;
-
- assert( _height > 0 && _height < 10000 ); // Just to play safe
- assert( _width > 0 && _width <= _bytes_per_row );
- }
-
- // Dispose of the offscreen buffer
- OffScreenBuffer::~OffScreenBuffer(void)
- {
- assert( graf_world != nil );
- DisposeGWorld(graf_world);
- graf_world = nil;
- pixmap = nil;
- _height = _width = _bytes_per_row = 0;
- }
-
-
-
- // Actual drawing - moving the picture from the
- // offscreen grafworld to the onscreen one, to the current port.
- // Remember to set a device to that of the offscreen
- // grafworld before CopyBits (in order to get grafworld's color table and
- // inverse color table to be used): see IM, Palette Manager
- // Also note, if a window we're going to draw on is partly/completely
- // obscured, then the visRgn, the visible region of the on-screen window,
- // is *not* a window rectangle. When we make the offscreen grafworld the
- // current grafport for CopyBits, CopyBits has no way of knowing then that
- // the true destination region may not be the whole where_rect at all. To make
- // CopyBits aware of the fact that the onscreen window maybe abscured, we
- // pass the visRgn of the onscreen window as a clipping region to CopyBits.
- // In that case CopyBits performs clipping just as if the onscreen window
- // where the current grafport.
- // Note, to be really precise, we ought to pass CopyBits() an intersection of
- // onscreen grafport's visRgn and clipRgn. Well, we take a shortcut here:
- // normally clipRgn is set to be the whole screen. Moreover, visRgn is clipped
- // already not only to the unobscured part of the window, but to the update
- // region as well. So using visRgn only should suffice....
- // Also make sure that the onscreen window has a palette roughly corresponding
- // to the CLUT of the grafworld (otherwise colors would be screwed up)
- // One can use a palette resource 0 to be used as a default palette for
- // dialogs/windows within the task
-
- // Note there is a different way to force color translation when drawing a picture
- // (seems to work better in 68k universe). One needs to create a grafworld, and
- // and draw on it within OpenPicture()...ClosePicture(). Then one can draw
- // thus recorded picture on the current window. The colors seem to get translated
- // correctly, and one doesn't need to worry about clipping, etc (as in the method
- // way explained above). One can use this technique to force 24->8 color
- // translation (with dithering) if the grafworld was created as 24-bit.
-
- // Also note a GetSubTable() ColorManager's function: it apparently allows
- // one to perform the necessary color translation/selection once, and
- // use only indices later!
-
- void OffScreenBuffer::draw(const Rect& where_rect, const Rect& from_rect)
- {
- // const BitMap * where_pixmap = &qd.thePort->portBits; // see below
- CGrafPtr old_port;
- GDHandle old_gdevice;
- GetGWorld(&old_port,&old_gdevice);
- const RgnHandle where_visible_region = old_port->visRgn;
- SetGWorld((CGrafPtr)graf_world,nil);
- // The following line is actually the same as
- // the one commented at the very top. However,
- // thePort stuff doesn't work under OpenDoc, but
- // the following line does.
- const BitMap * where_pixmap = &((GrafPtr)old_port)->portBits;
- //!!!Set_NewGrafWorld((GWorldPtr)graf_world); // Doesn't work: CW disposes of the object too early
- assert( LockPixels(pixmap) );
- CopyBits((const BitMap *)*pixmap, where_pixmap, &from_rect, &where_rect, /*ditherCopy*/ srcCopy, where_visible_region);
- SetGWorld(old_port,old_gdevice);
- UnlockPixels(pixmap);
- }
-
- // Clear the buffer
- void OffScreenBuffer::clear(void)
- {
- GrafPtr old_port = set_this_grafptr();
- EraseRect(&(**pixmap).bounds);
- SetPort(old_port);
- }
-
- // Optimize the OffScreen buffer for the faster
- // display on a given color device
- // That means, if the g_device and the offscreen world
- // are compatible (have the same depth) we "disable"
- // the color translation in CopyBits from this gworld
- // to an on-screen GWorld. We assume that gdevice color
- // tables are already set up appropriately (by the
- // palette manager, for example)
- Boolean OffScreenBuffer::optimize_color_worlds(const GDHandle g_device)
- {
- const PixMapHandle on_screen_pixmap = (**g_device).gdPMap;
- if( (**on_screen_pixmap).pixelSize != (**pixmap).pixelSize )
- return FALSE; // GWorlds are obviously very different...
- const CTabHandle on_screen_clut = (**on_screen_pixmap).pmTable;
- if( on_screen_clut == nil )
- return FALSE;
- CTabHandle off_screen_clut = (**pixmap).pmTable;
- assert( off_screen_clut != nil );
- if( (**on_screen_clut).ctSize < (**off_screen_clut).ctSize )
- return FALSE; // Color translation (reduction) is obviously necessary
- (**off_screen_clut).ctSeed = (**on_screen_clut).ctSeed; // That'll make CopyBits faster!
- return TRUE;
- }
-
-
- // Brute imposing of our color world on the
- // g_device: settip up its colortables by force
- // Chances are it would screw colors of all other
- // windows; but if we're the only window on the screen
- // or we don't care
- // If this imposition succeeds (the function returns
- // TRUE), our grafworld when blitted on screen would
- // certainly look at its (color) best
- Boolean OffScreenBuffer::impose_on_gdevice(GDHandle g_device)
- {
- const PixMapHandle on_screen_pixmap = (**g_device).gdPMap;
- if( (**g_device).gdType == fixedType )
- return FALSE; // CLUT on this device can't be changed
- // what about a direct device? Should we try to impose on it too?
-
- if( (**on_screen_pixmap).pixelSize != (**pixmap).pixelSize )
- return FALSE; // GWorlds are obviously very different...
- CTabHandle on_screen_clut = (**on_screen_pixmap).pmTable;
- if( on_screen_clut == nil )
- return FALSE;
- CTabHandle off_screen_clut = (**pixmap).pmTable;
- assert( off_screen_clut != nil );
- // Build a ReqListReq for a 256-entry CLUT
- short req_list_buffer[256+1];
- ReqListRec * const req_list = (ReqListRec *)req_list_buffer;
- req_list->reqLSize = sizeof(req_list_buffer)/sizeof(req_list_buffer[0]) - 1 - 1; // number of entries - 1
- for(register int i=0; i<=req_list->reqLSize; i++)
- req_list->reqLData[i] = i; // means move all CLUT entries
- //Debugger();
- RestoreEntries(off_screen_clut,on_screen_clut,req_list); // Foist our color table
- do_well( QDError() );
- (**off_screen_clut).ctSeed = (**on_screen_clut).ctSeed; // Make the tables look as if there are the same
- return TRUE;
- }
-